/*
 * // Bristleback plugin - Copyright (c) 2010 bristleback.googlecode.com
 * // ---------------------------------------------------------------------------
 * // This program is free software; you can redistribute it and/or modify it
 * // under the terms of the GNU Lesser General Public License as published by the
 * // Free Software Foundation; either version 3 of the License, or (at your
 * // option) any later version.
 * // This library is distributed in the hope that it will be useful,
 * // but without any warranty; without even the implied warranty of merchantability
 * // or fitness for a particular purpose.
 * // You should have received a copy of the GNU Lesser General Public License along
 * // with this program; if not, see <http://www.gnu.org/licenses/lgpl.html>.
 * // ---------------------------------------------------------------------------
 */

package pl.bristleback.server.bristle.binding;

import org.apache.log4j.Logger;
import org.jwebsocket.token.Token;
import pl.bristleback.server.bristle.binding.resolvers.BindingEngine;
import pl.bristleback.server.bristle.exceptions.BindingException;
import pl.bristleback.server.bristle.exceptions.BristleRuntimeException;

/**
 * This class wraps single binding operation. Object binders should be placed in action class and marked with {@link Bind} annotation.
 * Together with {@link Bind} annotation, it determines all details of the binding.
 * Object binders should not be instantiated by user, instead it is processed
 * and injected into action class by {@link pl.bristleback.server.bristle.binding.BindingsProcessor} implementation.
 * T represents type of object to bind.
 * <p/>
 * Created on: 2011-02-15 21:21:39 <br/>
 *
 * @author Wojciech Niemiec
 */
public class ObjectBinder<T> {
  private static Logger log = Logger.getLogger(ObjectBinder.class.getName());

  private BindingEngine engine;
  private NestedFieldInformation rootBean;
  private Class<T> beanClass;

  /**
   * Performs bindings operation, copying data from token to object using instructions given by corresponding {@link Bind} annotation.
   * Throws {@link pl.bristleback.server.bristle.exceptions.BindingException} if any validation error occurs.
   *
   * @param object object to bind.
   * @param token  property values.
   */
  public void bind(T object, Token token) {
    BindingResult<T> result = engine.bind(object, token, rootBean);
    if (result.hasErrors()) {
      throw new BindingException("Unhandled binding errors exists: " + result.getErrors().size(), result);
    }
  }

  /**
   * Performs bindings operation, copying data from token to object using instructions given by corresponding {@link Bind} annotation.
   * This version of bind method creates and returns new object instead of setting fields on parameter.
   * Throws {@link pl.bristleback.server.bristle.exceptions.BindingException} if any validation error occurs.
   *
   * @param token property values.
   * @return object passed as parameter, in case of invocation chain usage.
   */
  public T bind(Token token) {
    T object = createObject();
    bind(object, token);
    return object;
  }

  private T createObject() {
    try {
      return beanClass.newInstance();
    } catch (InstantiationException e) {
      log.error("Exception during binding object from token: " + e);
    } catch (IllegalAccessException e) {
      log.error("Exception during binding object from token: " + e);
    }
    throw new BristleRuntimeException("can not create instance of class: " + beanClass);
  }

  /**
   * Performs bindings operation, copying data from token to object using instructions given by corresponding {@link Bind} annotation.
   * Returns binding result object instead of passed object or binding exception.
   *
   * @param object object to bind.
   * @param token  property values.
   * @return binding result wrapper.
   */
  public BindingResult<T> bindWithResult(T object, Token token) {
    return engine.bind(object, token, rootBean);
  }

  public BindingEngine getEngine() {
    return engine;
  }

  public void setEngine(BindingEngine engine) {
    this.engine = engine;
  }

  public Class<T> getBeanClass() {
    return beanClass;
  }

  public void setBeanClass(Class<T> beanClass) {
    this.beanClass = beanClass;
  }

  public NestedFieldInformation getRootBean() {
    return rootBean;
  }

  public void setRootBean(NestedFieldInformation rootBean) {
    this.rootBean = rootBean;
  }
}
